<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <h3>原型链继承（父的实例作为子的原型）</h3>
    <pre>        
    function Person(name){
        this.name = name||'123';
    }
    Person.prototype.sayHi = function() {
        console.log('I am '+this.name);
    }

    function Student() {
        this.score = 100;
    }
    Student.prototype = new Person();  // 核心
    Student.prototype.nameAttr = 'haiXia';
    // Student.sayHi(); // 报错
    let son1Obj = new Student();
    son1Obj.sayHi();

    </pre>
    <pre>优点：
        简单易于实现，父类的新增的实例与属性子类都能访问
        缺点：
        可以在子类中增加实例属性，如果要新增加原型属性和方法需要在new 父类构造函数的后面
        无法实现多继承
        创建子类实例时，不能向父类构造函数中传参数</pre>

    <h3>借用构造函数继承(复制父类的实例属性给子类)</h3>
    <pre>优点：
        解决了子类构造函数向父类构造函数传递参数的问题
        可以实现多继承（call或者apply多个父类）
        缺点：
        方法都在构造函数中定义，无法复用
        不能继承原型属性/方法，只能继承父类的属性和方法</pre>
    
    <h3>实例继承（原型式继承）</h3>
    <pre>优点：
        不限制调用方式,简单易实现
        缺点：
        不能多次继承</pre>

    <h3>组合式继承</h3>
    <p>调用父类的构造函数，继承父类的属性，通过将父类实例作为子类原型，实现函数复用</p>
    <pre>优点：
        函数可以复用，不存在引用属性问题，可以继承属性和方法，并且可以继承原型的属性和方法
        缺点：
        调用了两次父类，产生了两个实例</pre>

    <h3>ES6继承</h3>
    <pre>使用关键字先创建父类的实例对象this，最后在子类class中修改this</pre>
    <script>
        // 父类
        function Person(name){
            this.name = name||'123';
        }
        Person.prototype.sayHi = function() {
            console.log('I am '+this.name);
        }

        // 1. 原型链继承（父的实例作为子的原型）
        function Student() {
            this.score = 100;
        }
        Student.prototype = new Person();  // 核心
        Student.prototype.nameAttr = 'haiXia';
        // Student.sayHi(); // 报错
        let son1Obj = new Student();
        son1Obj.sayHi();
        console.log('son1Obj.nameAttr:',son1Obj.nameAttr,', son1Obj instanceof Person：',son1Obj instanceof Person, ', son1Obj instanceof Student',son1Obj instanceof Student);


        // 2. 构造函数继承
        function son2(name, age, sex){
            Person.call(this, name);   // Person.call(this, 'jer');
            this.age = age;
            this.sex = sex;
        }
        console.log('>>>', typeof son2, son2 instanceof Function);
        son2.__proto__ = Person.prototype;
        son2.sayHi()
        // let son2Obj = new son2('son2', 18, 'woman');
        // son2Obj.sayHi(); // 报错
        // console.log('son2Obj',son2Obj.nameAttr);  // undefined 没访问到
        // console.log('son2Obj instanceof Person：',son2Obj instanceof Person);
        // console.log('son2Obj instanceof son2：',son2Obj instanceof son2);


        // 3. 实例继承（原型式继承）
        function son3(name){
            let instance = new Person();
            instance.name = name || 'son3a';
            return instance;
        }

        let son3Obj = new son3('son3');
        // son3Obj.sayHi();
        // console.log('son3Obj',son3Obj.nameAttr); // undefined 没访问到
        // console.log('son3Obj instanceof Person', son3Obj instanceof Person);
        // console.log('son3Obj instanceof son3：', son3Obj instanceof son3);

        // 4. 组合式继承
        function son4(name, age){
            Person.call(this, name);
            this.age = age;
        }

        son4.prototype = new Person();
        son4.prototype.constructor = son4;
        let son4Obj = new son4('son4');
        // son4Obj.sayHi();
        // console.log('son4Obj',son4Obj.nameAttr); // undefined 没访问到
        // console.log('son4Obj instanceof Person', son4Obj instanceof Person);
        // console.log('son4Obj instanceof son4：', son4Obj instanceof son4);

        // 5. ES6继承
        class PeopleEs6 {
            constructor(name='wang', age=27){
                this.name = name;
                this.age = age;
            }

            eat(){
                console.log(this.name + this.age + 'eat===>ES6');
            }
        }

        class son5 extends PeopleEs6 {
            constructor(name='son5a', age='32'){
                super(name, age);
            }

            eat(){
                super.eat();
            }
        }

        let son5Obj = new son5('tian')
        // son5Obj.eat();
        // console.log('son5',son5Obj instanceof PeopleEs6);

    </script>
</body>
</html>